﻿using LPBToolsLib;
using System;
using System.Collections.Generic;
using System.Text;

/*
 DEx通讯服务数据包中携带的Option数据对象
DExOpt： 现有Option命令类型
DExOptionBase：Option基础类，用于容纳收到的数据，进行初步分析，并根据本身数据解析成为对应对象。
    以下对象都继承于该对象
  
DExOption_UploadRegValue_0x03:  下位机上传本机寄存器数据，需要提交寄存器起始地址及寄存器数据
DExOption_DownLoadWriteOn_0x05: 上位机向下位机下达写入线圈数据
Opt_DownLoadWriteReg_0x10：     上位机向下位机下达写入寄存器命令，需要待写入寄存器起始地址及寄存器数据
 
 */
namespace StationNF
{
    /// <summary>
    /// 所有Option数据段数据结构：所有Option必须由2部分组成，分别是数据头及数据体
    /// 头：长度4字节
    ///  00 03 00 08 00 00 00 00  
    ///  [00-01]:       Option命令：DExOpt
    ///  [02-03]:       本Option段数据长度，包括数据头
    ///  [04-N]:        Option携带的数据
    /// </summary>
    public enum DExOpt
    {
        None = 0,
        Opt_DeclarePLCType_0x01 = 0x01,
        // 下位机向上位机声明本PLC的类型
        // Option携带的数据体的数据结构:
        // [0-1]：下位机PLC类型代码
        // [2-3]：常用寄存器起始地址
        // [4-5]：常用寄存器数量
        Opt_UploadRegValue_0x03 = 0x03,
        // 下位机上传本机寄存器数据，需要提交寄存器起始地址及寄存器数据
        // Option携带的数据体的数据结构:
        // [0-1]：寄存器读取起始地址
        // [2-3]：寄存器读取数量
        // [4-N]：已经读出的寄存器值
        Opt_DownLoadWriteOn_0x05 = 0x05,
        // 上位机向下位机下达写入线圈数据
        // Option携带的数据体的数据结构:
        // [0]：待写入的线圈序号
        // [1]：要写入的线圈值0或1
        Opt_DownLoadWriteReg_0x10 = 0x10,
        // 上位机向下位机下达写入寄存器命令，需要待写入寄存器起始地址及寄存器数据
        // Option携带的数据体的数据结构:
        // [0-1]：寄存器写入起始地址
        // [2-3]：寄存器写入数量
        // [4-N]：待写入的寄存器值 
    }
    /// <summary>
    /// Option基础类，用于容纳收到的数据，进行初步分析，并根据本身数据解析成为对应对象。
    /// 并将剩余数据分解出，从而重新创建新节点
    /// </summary>
    public class DExOptionBase : ContainerBase
    {
        public Recorder_Error SysErr { get; set; } = null;
        public Recorder_RunLog RunLog { get; set; } = null;
        protected List<byte> tOptDatas { get; } = new List<byte>();

        /// <summary>
        /// 本节点对应的底层二进制数据流
        /// </summary>
        public byte[] OptionDatas { get { return tOptDatas.ToArray(); } }
        //public byte[] OptDatas { get { return tOptDatas.ToArray(); } }        

        public virtual bool OptReady
        {
            get
            {
                if (tOptDatas.Count >= 4)
                {
                    int iX = (tOptDatas[2] << 8) | tOptDatas[3];
                    return (Enum.IsDefined(typeof(DExOpt), (tOptDatas[0] << 8) | tOptDatas[1]))
                        && (((tOptDatas[2] << 8) | tOptDatas[3]) == tOptDatas.Count);
                }
                return false;
            }
        }

        public DExOpt OptCommand
        {
            get
            {
                if (OptReady)
                    return (DExOpt)((tOptDatas[0] << 8) | tOptDatas[1]);
                return DExOpt.None;
            }
        }

        public int OptDataslen
        {
            get
            {
                if (OptReady)
                    return ((tOptDatas[2] << 8) | tOptDatas[3]);
                return 0;
            }
        }

        protected byte[] DatasofTail { get; private set; } = null;

        /// <summary>
        /// 获得下一节点对象
        /// </summary>
        /// <returns>成功获得返回对象，否则返回null</returns>
        public DExOptionBase OptionOfNext()
        {
            if (DatasofTail != null)
            {
                DExOptionBase tResult = new DExOptionBase(DatasofTail);
                if (tResult.OptReady)
                    return tResult;
            }
            return null;
        }

        /// <summary>
        /// 用于根据收到的数据流创建命令
        /// </summary>
        /// <param name="tDestOptDatas"></param>
        public DExOptionBase(byte[] tDestOptDatas)
        {
            tOptDatas.Clear();
            DatasofTail = null;
            if ((tDestOptDatas != null) && (tDestOptDatas.Length >= 4)
                && (Enum.IsDefined(typeof(DExOpt), (tDestOptDatas[0] << 8) | tDestOptDatas[1]))
                && (((tDestOptDatas[2] << 8) | tDestOptDatas[3]) <= tDestOptDatas.Length)
                && (((tDestOptDatas[2] << 8) | tDestOptDatas[3]) >= 4))
            {
                int iDataLen = (tDestOptDatas[2] << 8) | tDestOptDatas[3];
                for (int i = 0; i < iDataLen; i++)
                    tOptDatas.Add(tDestOptDatas[i]);
                List<byte> tBuf = new List<byte>();
                if (iDataLen < tDestOptDatas.Length)
                    for (int i = iDataLen; i < tDestOptDatas.Length; i++)
                        tBuf.Add(tDestOptDatas[i]);
                if (tBuf.Count > 0)
                    DatasofTail = tBuf.ToArray();
            }
        }
        /// <summary>
        /// 根据需要创建空命令
        /// </summary>
        /// <param name="eOptCommand"></param>
        public DExOptionBase(DExOpt eOptCommand)
        {
            tOptDatas.Clear();
            tOptDatas.Add(0);
            tOptDatas.Add((byte)eOptCommand);
            tOptDatas.Add(0);
            tOptDatas.Add(4);
        }
        /// <summary>
        /// 根据需要追加数据到命令中，仅用于创建数据流
        /// </summary>
        /// <param name="tDestOptDatas"></param>
        /// <returns></returns>
        protected int AppOptDatas(byte[] tDestOptDatas)
        {
            if ((tDestOptDatas != null) && (tDestOptDatas.Length > 0)
                && (tDestOptDatas.Length < 0xFFF0))
            {
                int iDLen = 4;
                if (tOptDatas.Count > 4)
                    tOptDatas.RemoveRange(4, tOptDatas.Count);
                for (int i = 0; i < tDestOptDatas.Length; i++)
                {
                    iDLen++;
                    tOptDatas.Add(tDestOptDatas[i]);
                }
                tOptDatas[2] = (byte)((iDLen >> 8) | 0xFF);
                tOptDatas[3] = (byte)((iDLen >> 0) | 0xFF);
            }
            return tOptDatas.Count;
        }

        /// <summary>
        /// 根据创建的Opt，获得对应的传输数据流
        /// </summary>
        /// <returns></returns>
        public byte[] getRTxDatas()
        {
            tOptDatas[2] = (byte)((tOptDatas.Count >> 8) & 0xFF);
            tOptDatas[3] = (byte)((tOptDatas.Count >> 0) & 0xFF);
            return tOptDatas.ToArray();
        }
    }

    public class DExOption_UploadRegValue_0x03 : DExOptionBase
    {
        public static DExOption_UploadRegValue_0x03 createOption(DExOptionBase tOption)
        {
            if ((tOption != null) && tOption.OptReady && (tOption.OptCommand == DExOpt.Opt_UploadRegValue_0x03))
                return new DExOption_UploadRegValue_0x03(tOption.OptionDatas);
            return null;
        }
        /// <summary>
        /// 根据输入的数据流，直接创建节点任务对象
        /// </summary>
        /// <param name="tDestOptDatas"></param>
        public DExOption_UploadRegValue_0x03(byte[] tDestOptDatas) : base(tDestOptDatas)
        { }
        /// <summary>
        /// 创建空的任务对象
        /// </summary>
        public DExOption_UploadRegValue_0x03() : base(DExOpt.Opt_UploadRegValue_0x03)
        {
            tOptDatas.AddRange(new byte[4]);
            tOptDatas[3] = 8;
        }

        /// <summary>
        /// 创建目标包括目标数据的任务对象
        /// </summary>
        /// <param name="iRegStar"></param>
        /// <param name="tRegValues"></param>
        public DExOption_UploadRegValue_0x03(ushort iRegStar, ushort[] tRegValues) : this()
        {
            UploadReadRegStarAddr = iRegStar;
            UploadReadRegValues = tRegValues;
        }
        public override bool OptReady
        {
            get
            {
                return base.OptReady && (tOptDatas.Count >= 8)
                    && ((2 * ((tOptDatas[6] << 8) | tOptDatas[7]) + 8) == ((tOptDatas[2] << 8) | tOptDatas[3]));
            }
        }
        /// <summary>
        ///  Option携带的数据的数据结构:
        ///  [0-1]：寄存器读取起始地址
        /// </summary>
        public int UploadReadRegStarAddr
        {
            get
            {
                if (base.OptReady && (tOptDatas.Count >= 8))
                    return ((tOptDatas[4] << 8) | tOptDatas[5]);
                return 0;
            }
            set
            {
                if (base.OptReady && (tOptDatas.Count >= 8))
                {
                    tOptDatas[4] = (byte)((value >> 8) & 0xFF);
                    tOptDatas[5] = (byte)((value >> 0) & 0xFF);
                }
            }
        }
        /// <summary>
        ///  Option携带的数据的数据结构:
        ///  [2-3]：寄存器读取数量
        ///  注意：本Option段数据长度=2*寄存器读取数量+8
        /// </summary>
        public int UploadReadRegCount
        {
            get
            {
                if (base.OptReady && (tOptDatas.Count >= 8))
                    return ((tOptDatas[6] << 8) | tOptDatas[7]);
                return 0;
            }
            private set
            {
                if (base.OptReady && (tOptDatas.Count >= 8))
                {
                    tOptDatas[2] = (byte)(((8 + 2 * value) >> 8) & 0xFF);
                    tOptDatas[3] = (byte)(((8 + 2 * value) >> 0) & 0xFF);
                    tOptDatas[6] = (byte)((value >> 8) & 0xFF);
                    tOptDatas[7] = (byte)((value >> 0) & 0xFF);
                    while (tOptDatas.Count < 8 + value * 2)
                        tOptDatas.Add(0);
                    while (tOptDatas.Count > 8 + value * 2)
                        tOptDatas.RemoveAt(tOptDatas.Count - 1);
                }
            }
        }

        public ushort[] UploadReadRegValues
        {
            get
            {
                List<ushort> tBuf = new List<ushort>();
                if (base.OptReady && (UploadReadRegCount * 2 == OptDataslen - 8))
                    for (int i = 0; i < UploadReadRegCount; i++)
                        tBuf.Add((ushort)((tOptDatas[i * 2 + 8] << 8) | tOptDatas[i * 2 + 9]));
                return tBuf.ToArray();
            }
            set
            {
                if ((value != null) && (value.Length > 0))
                {
                    UploadReadRegCount = value.Length;
                    for (int i = 0; i < value.Length; i++)
                    {
                        tOptDatas[i * 2 + 8] = (byte)((value[i] >> 8) & 0xFF);
                        tOptDatas[i * 2 + 9] = (byte)((value[i] >> 0) & 0xFF);
                    }
                }
            }
        }
    }

    public class DExOption_DeclarePLCType_0x01 : DExOptionBase
    {
        public static DExOption_DeclarePLCType_0x01 createOption(DExOptionBase tOption)
        {
            if ((tOption != null) && tOption.OptReady && (tOption.OptCommand == DExOpt.Opt_DeclarePLCType_0x01))
                return new DExOption_DeclarePLCType_0x01(tOption.OptionDatas);
            return null;
        }
        /// <summary>
        /// 根据输入的数据流，直接创建节点任务对象
        /// </summary>
        /// <param name="tDestOptDatas"></param>
        public DExOption_DeclarePLCType_0x01(byte[] tDestOptDatas) : base(tDestOptDatas)
        { }
        /// <summary>
        /// 创建空的任务对象
        /// </summary>
        public DExOption_DeclarePLCType_0x01() : base(DExOpt.Opt_DeclarePLCType_0x01)
        {
            tOptDatas.AddRange(new byte[6]);
            tOptDatas[3] = 10;
        }

        /// <summary>
        /// 创建目标包括目标数据的任务对象
        /// </summary>
        /// <param name="iRegStar"></param>
        /// <param name="tRegValues"></param>
        public DExOption_DeclarePLCType_0x01(ushort iPLCType, ushort iRegStar, ushort tRegCount) : this()
        {
            PLCRegStarAddr = iRegStar;
            PLCRegCount = tRegCount;
        }
        public override bool OptReady
        {
            get
            {
                return base.OptReady && (tOptDatas.Count >= 10);
            }
        }
        /// <summary>
        ///  Option携带的数据的数据结构:
        ///  [0-1]：下位机PLC类型代码
        /// </summary>
        public int PLCRType
        {
            get
            {
                if (OptReady)
                    return ((tOptDatas[4] << 8) | tOptDatas[5]);
                return 0;
            }
            set
            {
                if (OptReady)
                {
                    tOptDatas[4] = (byte)((value >> 8) & 0xFF);
                    tOptDatas[5] = (byte)((value >> 0) & 0xFF);
                }
            }
        }
        /// <summary>
        ///  Option携带的数据的数据结构:
        /// [2-3]：常用寄存器起始地址
        /// </summary>
        public int PLCRegStarAddr
        {
            get
            {
                if (OptReady)
                    return ((tOptDatas[6] << 8) | tOptDatas[7]);
                return 0;
            }
            set
            {
                if (OptReady)
                {
                    tOptDatas[6] = (byte)((value >> 8) & 0xFF);
                    tOptDatas[7] = (byte)((value >> 0) & 0xFF);
                }
            }
        }
        /// <summary>
        ///  Option携带的数据的数据结构:
        ///  [4-5]：常用寄存器数量
        /// </summary>
        public int PLCRegCount
        {
            get
            {
                if (OptReady)
                    return ((tOptDatas[8] << 8) | tOptDatas[9]);
                return 0;
            }
            set
            {

                if (OptReady)
                {
                    tOptDatas[8] = (byte)((value >> 8) & 0xFF);
                    tOptDatas[9] = (byte)((value >> 0) & 0xFF);
                }
            }
        }
    }

    public class DExOption_DownLoadWriteOn_0x05 : DExOptionBase
    {
        public static DExOption_DownLoadWriteOn_0x05 createOption(DExOptionBase tOption)
        {
            if ((tOption != null) && tOption.OptReady && (tOption.OptCommand == DExOpt.Opt_DownLoadWriteOn_0x05))
                return new DExOption_DownLoadWriteOn_0x05(tOption.OptionDatas);
            return null;
        }
        /// <summary>
        /// 根据输入的数据流，直接创建节点任务对象
        /// </summary>
        /// <param name="tDestOptDatas"></param>
        public DExOption_DownLoadWriteOn_0x05(byte[] tDestOptDatas) : base(tDestOptDatas)
        { }
        public DExOption_DownLoadWriteOn_0x05() : base(DExOpt.Opt_DownLoadWriteOn_0x05)
        {
            tOptDatas.AddRange(new byte[2]);
            tOptDatas[3] = 6;
        }

        public DExOption_DownLoadWriteOn_0x05(byte iYIndex, bool iValues) : this()
        {
            YIndex = iYIndex;
            Value = iValues;
        }

        public byte YIndex
        {
            get
            {
                if (base.OptReady && (tOptDatas.Count == 6))
                    return tOptDatas[4];
                return 0;
            }
            set
            {
                if (base.OptReady && (tOptDatas.Count == 6))
                {
                    tOptDatas[4] = value;
                }
            }
        }
        public bool Value
        {
            get
            {
                if (base.OptReady && (tOptDatas.Count == 6))
                    return tOptDatas[5] != 0;
                return false;
            }
            set
            {
                if (base.OptReady && (tOptDatas.Count == 6))
                    tOptDatas[5] = (byte)(value ? 1 : 0);
            }
        }
    }

    public class DExOption_DownLoadWriteReg_0x10 : DExOptionBase
    {
        public static DExOption_DownLoadWriteReg_0x10 createOption(DExOptionBase tOption)
        {
            if ((tOption != null) && tOption.OptReady && (tOption.OptCommand == DExOpt.Opt_DownLoadWriteReg_0x10))
                return new DExOption_DownLoadWriteReg_0x10(tOption.OptionDatas);
            return null;
        }

        public DExOption_DownLoadWriteReg_0x10(byte[] tDestOptDatas) : base(tDestOptDatas)
        { }
        public DExOption_DownLoadWriteReg_0x10() : base(DExOpt.Opt_DownLoadWriteReg_0x10)
        {
            tOptDatas.AddRange(new byte[4]);
            tOptDatas[3] = 8;
        }

        public DExOption_DownLoadWriteReg_0x10(ushort iRegStar, ushort[] tRegValues) : this()
        {
            DownLoadWriteRegStarAddr = iRegStar;
            DownLoadWriteRegValues = tRegValues;
        }


        public int DownLoadWriteRegStarAddr
        {
            get
            {
                if (base.OptReady && (tOptDatas.Count >= 8))
                    return ((tOptDatas[4] << 8) | tOptDatas[5]);
                return 0;
            }
            set
            {
                if (tOptDatas.Count >= 8)
                {
                    tOptDatas[4] = (byte)((value >> 8) & 0xFF);
                    tOptDatas[5] = (byte)((value >> 0) & 0xFF);
                }
            }
        }
        public int DownLoadWriteRegCount
        {
            get
            {
                if (base.OptReady && (tOptDatas.Count >= 8))
                    return ((tOptDatas[6] << 8) | tOptDatas[7]);
                return 0;
            }
            private set
            {
                if (base.OptReady && (tOptDatas.Count >= 8))
                {
                    tOptDatas[2] = (byte)(((8 + 2 * value) >> 8) & 0xFF);
                    tOptDatas[3] = (byte)(((8 + 2 * value) >> 0) & 0xFF);
                    tOptDatas[6] = (byte)((value >> 8) & 0xFF);
                    tOptDatas[7] = (byte)((value >> 0) & 0xFF);
                    while (tOptDatas.Count < 8 + value * 2)
                        tOptDatas.Add(0);
                    while (tOptDatas.Count > 8 + value * 2)
                        tOptDatas.RemoveAt(tOptDatas.Count - 1);
                        //tOptDatas.RemoveRange(8 + value * 2, tOptDatas.Count - 8 + value * 2);
                }
            }
        }

        public ushort[] DownLoadWriteRegValues
        {
            get
            {
                List<ushort> tBuf = new List<ushort>();
                if (base.OptReady && (DownLoadWriteRegCount * 2 == OptDataslen - 8))
                    for (int i = 0; i < DownLoadWriteRegCount; i++)
                        tBuf.Add((ushort)((tOptDatas[i * 2 + 8] << 8) | tOptDatas[i * 2 + 9]));
                return tBuf.ToArray();
            }
            set
            {
                if ((value != null) && (value.Length >= 0))
                {
                    DownLoadWriteRegCount = value.Length;
                    for (int i = 0; i < value.Length; i++)
                    {
                        tOptDatas[i * 2 + 8] = (byte)((value[i] >> 8) & 0xFF);
                        tOptDatas[i * 2 + 9] = (byte)((value[i] >> 0) & 0xFF);
                    }
                }
            }
        }
    }
}
